/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#ifndef __VARRAY_H__
#define __VARRAY_H__

#include <vector>
#include "../Exceptions/vExceptions.h"
#include "../Macros/vMemoryMacros.h"
#include "../Macros/vLogMacros.h"


namespace Vendetta {
	template<class T> class vArray {
	public:
		vArray() { }

		explicit vArray(vUInt n, const T& value = T()) {
			mData = std::vector<T>(n, value);
		}

		vArray(const vArray& arr) {
			mData = arr.mData;
		}

		~vArray() { 
			mData.clear();
		}

		vArray& operator=(const vArray& arr) {
			if (this != &arr) {
				mData = arr.mData;
			}

			return *this;
		}

		vArray& operator<<(const T& val) {
			mData.push_back(val);

			return *this;
		}

		T& operator[](vUInt uiPos) {
			try {
				return mData[uiPos];
			} catch (std::exception&) {
				throw vFatalException("Index out of bounds. In vArray::operator[].");
			}
		}
		
		const T& operator[](vUInt uiPos) const {
			try {
				return mData[uiPos];
			} catch (std::exception&) {
				throw vFatalException("Index out of bounds. In vArray::const operator[]");
			}
		}

		vUInt Add(const T& val, vUInt uiPos) {
			vUInt uiRet;

			V_CHECK_MSG(uiPos > mData.size(), "Index out of bounds. In vArray::Add");
			if (uiPos == mData.size()) {
				mData.push_back(val);
				uiRet = static_cast<vUInt>(mData.size()) - 1;
			} else {
				mData.insert(mData.begin() + uiPos, val);
				uiRet = uiPos;
			}

			return uiRet;
		}

		vVoid Remove(vUInt uiPos) {
			V_CHECK_MSG(uiPos >= mData.size(), "Index out of bounds. In vArray::Remove");
			mData.erase(mData.begin() + uiPos);
		}

		vVoid RemoveFirst() {
			try {
				mData.erase(mData.begin());
			} catch (std::exception&) {
				throw vFatalException("Index out of bounds. Empty array. In vArray::RemoveFirst.");
			}
		}

		vBool Contains(const T& elem) {
			for (std::vector<T>::iterator it = mData.begin(); it != mData.end(); it++) {
				if ((*it) == elem) {
					return true;
				}
			}

			return false;
		}

		vUInt Find(const T& elem) {
			vUInt i = 0;

			for (std::vector<T>::iterator it = mData.begin(); it != mData.end(); it++, i++) {
				if ((*it) == elem) {
					return i;
				}
			}

			throw vFatalException("Element not found in vArray::Find.");
		}

		inline vUInt AddAsLast(const T& val) {
			mData.push_back(val);

			return static_cast<vUInt>(mData.size() - 1);
		}

		inline vUInt AddAsFirst(const T& val) {
			mData.insert(mData.begin(), val);

			return 0;
		}		

		inline vUInt Size() const {
			return static_cast<vUInt>(mData.size());
		}

		inline vVoid Clear() {
			mData.clear();
		}

		inline vBool IsEmpty() const {
			return mData.empty();
		}

		inline vVoid RemoveLast() {
			mData.pop_back();
		}

		static vArray FromRawArray(const T* arr, vUInt size) {
			vArray<T> retArray;

			for (vUInt i = 0; i < size; i++) {
				retArray.AddAsLast(arr[i]);
			}

			return retArray;
		}

	private:
		std::vector<T> mData;
	};
}

#endif //__VARRAY_H__